﻿using Common;
using Common.FrontEnd;
using FrontEnd;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Util;

namespace Linguist.Acoustic.Tiedstate
{
    /// <summary>
    /// Represents a concrete implementation of a simple {@link Senone senone}. A simple senone is a set of probability
    /// density functions implemented as a Gaussian mixture.
    /// <p/>
    /// All scores and weights are maintained in LogMath log base.
    /// </summary>
    public class GaussianMixture : ScoreCachingSenone
    {
        // these data element in a senone may be shared with other senones
        // and therefore should not be written to.
        private float[] logMixtureWeights;
        private MixtureComponent[] mixtureComponents;
        private long id;

        private LogMath logMath;


        /**
        /// Creates a new senone from the given components.
         *
        /// @param logMixtureWeights the mixture weights for this senone in LogMath log base
        /// @param mixtureComponents the mixture components for this senone
         */
        public GaussianMixture(float[] logMixtureWeights,
                               MixtureComponent[] mixtureComponents, long id) 
        {

            Trace.Assert(mixtureComponents.Length == logMixtureWeights.Length);

            logMath = LogMath.getLogMath();
            this.mixtureComponents = mixtureComponents;
            this.logMixtureWeights = logMixtureWeights;
            this.id = id;
        }


        /**
        /// Dumps this senone.
         *
        /// @param msg annotation message
         */
        public void dump(String msg) 
        {
            Trace.WriteLine(msg + " GaussianMixture: ID " + getID());
        }

        /**
        /// Determines if two objects are equal
         *
        /// @param o the object to compare to this.
        /// @return true if the objects are equal
         */
        override
        public Boolean Equals(Object o) 
        {
            if (!(o is ISenone)) 
            {
                return false;
            }
            ISenone other = (ISenone) o;
            return this.getID() == other.getID();
        }


        /**
        /// Returns the hashcode for this object
         *
        /// @return the hashcode
         */
        public int hashCode() 
        {
            long id = getID();
            int high = (int) ((id >> 32));
            int low = (int) (id);
            return high + low;
        }



        public long getID() 
        {
            return id;
        }


        /**
        /// Retrieves a string form of this object
         *
        /// @return the string representation of this object
         */
        override
        public String ToString() 
        {
            return "senone id: " + getID();
        }

        override public float calculateScore(IData feature) 
        {
            if (feature is DoubleData)
                Trace.WriteLine("DoubleData conversion required on mixture level!");

            float[] featureVector = FloatData.toFloatData(feature).getValues();

            float logTotal = LogMath.LOG_ZERO;
            for (int i = 0; i < mixtureComponents.Length; i++) {
                // In linear form, this would be:
                //
                // Total += Mixture[i].score/// MixtureWeight[i]
                logTotal = logMath.addAsLinear(logTotal,
                        mixtureComponents[i].getScore(featureVector) + logMixtureWeights[i]);
            }

            return logTotal;
        }


        /**
        /// Calculates the scores for each component in the senone.
         *
        /// @param feature the feature to score
        /// @return the LogMath log scores for the feature, one for each component
         */
        public float[] calculateComponentScore(IData feature) 
        {
            if (feature is DoubleData)
                Trace.WriteLine("DoubleData conversion required on mixture level!");

            float[] featureVector = FloatData.toFloatData(feature).getValues();

            float[] logComponentScore = new float[mixtureComponents.Length];
            for (int i = 0; i < mixtureComponents.Length; i++) {
                // In linear form, this would be:
                //
                // Total += Mixture[i].score/// MixtureWeight[i]
                logComponentScore[i] = mixtureComponents[i].getScore(featureVector) + logMixtureWeights[i];
            }

            return logComponentScore;
        }


        public MixtureComponent[] getMixtureComponents() 
        {
            return mixtureComponents;
        }


        /** @return the dimension of the modeled feature space */
        public int dimension() {
            return mixtureComponents[0].getMean().Length;
        }


        /** @return the number of component densities of this <code>GaussianMixture</code>. */
        public int numComponents() {
            return mixtureComponents.Length;
        }


        public float[] getLogMixtureWeights(){
    	    return this.logMixtureWeights;
        }
    

        /** @return the (linearly scaled) mixture weights of the component densities */
        public float[] getComponentWeights() {
            float[] mixWeights = new float[getMixtureComponents().Length];
            for (int i = 0; i < mixWeights.Length; i++)
                mixWeights[i] = (float) logMath.logToLinear(logMixtureWeights[i]);

            return mixWeights;
        }

        /**
        /// @return the (log-scaled) mixture weight of the component density
        /// <code>index</code> */
        public float getLogComponentWeight(int index) {
            return logMixtureWeights[index];
        }

    }
}
